{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a911dbb-e9b6-402b-a2d3-416adb12ba0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "import bs4\n",
    "import getpass\n",
    "import os\n",
    "\n",
    "\n",
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "from langchain_core.messages import AIMessage, HumanMessage, BaseMessage\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_huggingface import HuggingFaceEmbeddings\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain.prompts import ChatPromptTemplate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "13133579-00df-4800-b1ba-214ba5d99e2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      " ········\n"
     ]
    }
   ],
   "source": [
    "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n",
    "llm = ChatOpenAI(model=\"gpt-4o-mini\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c05a41c1-161c-45b3-a404-84fa790b53c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "embeddings_provider = HuggingFaceEmbeddings(model_name=\"sentence-transformers/all-mpnet-base-v2\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a3846165-28a8-4710-9d50-046cf754743f",
   "metadata": {},
   "outputs": [],
   "source": [
    "loader = WebBaseLoader(\n",
    "    [\"https://lilianweng.github.io/posts/2023-06-23-agent/\"\n",
    "     ]\n",
    ")\n",
    "\n",
    "docs = loader.load()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "af21b10b-bb46-4674-ba5d-d431d89843f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "text_splitter = RecursiveCharacterTextSplitter()\n",
    "document_chunks = text_splitter.split_documents(docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b444643b-3840-480f-bf78-230b26af9740",
   "metadata": {},
   "outputs": [],
   "source": [
    "vector_store = FAISS.from_documents(document_chunks, embeddings_provider)\n",
    "retriever = vector_store.as_retriever()\n",
    "output_parser = StrOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b782f90c-5805-45c0-8ab1-dd56811f7711",
   "metadata": {},
   "outputs": [],
   "source": [
    "contextualize_q_system_prompt = \"\"\"Given a chat history and the latest user question \\\n",
    "which might reference context in the chat history, formulate a standalone question \\\n",
    "which can be understood without the chat history. Do NOT answer the question, \\\n",
    "just reformulate it if needed and otherwise return it as is.\"\"\"\n",
    "\n",
    "contextualize_q_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", contextualize_q_system_prompt),\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "592c6bc6-d43d-4dff-9e2f-c33126aa5293",
   "metadata": {},
   "outputs": [],
   "source": [
    "contextualize_q_chain = contextualize_q_prompt | llm | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "c239416c-ade7-480b-be49-96a327ca32d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "qa_system_prompt = \"\"\"You are an assistant for question-answering tasks. \\\n",
    "Use the following pieces of retrieved context to answer the question. \\\n",
    "If you don't know the answer, just say that you don't know. \\\n",
    "Use three sentences maximum and keep the answer concise.\\\n",
    "\n",
    "{context}\"\"\"\n",
    "qa_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", qa_system_prompt),\n",
    "        MessagesPlaceholder(variable_name=\"chat_history\"),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c28315df-1999-46d0-8d04-f8c90ac8e2a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def contextualized_question(input: dict):\n",
    "    if input.get(\"chat_history\"):\n",
    "        return contextualize_q_chain\n",
    "    else:\n",
    "        return input[\"question\"]\n",
    "\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "04a703c1-1830-4e8c-b018-9bdb383fd990",
   "metadata": {},
   "outputs": [],
   "source": [
    "rag_chain = (\n",
    "        RunnablePassthrough.assign(\n",
    "            context=contextualized_question | retriever | format_docs\n",
    "        )\n",
    "        | qa_prompt\n",
    "        | llm\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "7508be31-de6c-49e4-9d3a-29ba99ad8c2b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A large language model (LLM) is an artificial intelligence system designed to understand and generate human-like text based on the input it receives. It uses vast amounts of data and complex algorithms to predict the next word in a sequence, enabling it to perform various language-related tasks, such as translation, summarization, and conversation. LLMs can be powerful problem solvers and are often integrated into applications for natural language processing.\n"
     ]
    }
   ],
   "source": [
    "chat_history = []\n",
    "\n",
    "question = \"What is a large language model?\"\n",
    "ai_msg = rag_chain.invoke({\"question\": question, \"chat_history\": chat_history})\n",
    "print(ai_msg.content)\n",
    "chat_history.extend([HumanMessage(content=question), AIMessage(content=ai_msg.content)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c668bb32-9830-49e8-9313-46e581ff71c0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The term \"large\" in large language model refers to both the size of the model itself and the volume of data it is trained on. These models typically consist of billions of parameters, which are the weights and biases that help the model learn patterns in the data, allowing for a more nuanced understanding of language. Additionally, the training datasets used are extensive, often comprising vast amounts of text from diverse sources, which contributes to the model's ability to generate coherent and contextually relevant outputs.\n"
     ]
    }
   ],
   "source": [
    "second_question = \"Can you explain the reasoning behind calling it large?\"\n",
    "second_answer = rag_chain.invoke({\"question\": second_question, \"chat_history\": chat_history})\n",
    "print(second_answer.content)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "chapter10",
   "language": "python",
   "name": "chapter10"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
